﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace QstkCore.Util
{
    public static class MathUtil
    {
        public static double NanSum(this IEnumerable<double> input)
        {
            return input.Sum(x => (double.IsNaN(x)) ? 0 : x);
        }

        public static float StdDev(this IEnumerable<float> input)
        {
            float standardDev = 0;
            var inputList = input as IList<float> ?? input.ToList();
            var inputLength = inputList.Count();
            if (inputList.Any() && inputLength > 1)
            {
                //Compute the Average      
                float mean = inputList.AsParallel().Average();

                standardDev = (float) inputList.AsParallel().Aggregate(
                    // initialize subtotal.
                    0.0d,

                    // do this on each thread
                    (subtotal, item) => subtotal + Math.Pow((item - mean), 2),

                    // aggregate results after all threads are done.
                    (total, thisThread) => total + thisThread,

                    // perform standard deviation calc on the aggregated result.
                    finalSum => Math.Sqrt((finalSum / (inputLength - 1)))
                );
            }
            return standardDev;
        }

        public static IEnumerable<double> CumulativeProduct(this IEnumerable<double> input)
        {
            double product = 1;
            foreach (var val in input)
            {
                product *= val;
                yield return product;
            }
        }

        public static double[,] CumulativeProduct(this double[,] input, int axis = 0)
        {
            var results = new double[input.GetLength(0), input.GetLength(1)];

            if (axis == 0)
            {
                Parallel.ForEach(ParallelEnumerable.Range(0, input.GetLength(1)),
                        j => results[0, j] = input[0, j]);

                foreach(int i in Enumerable.Range(1, input.GetLength(0) - 1))
                {
                    for (int j = 0; j < input.GetLength(1); j++)
                        results[i, j] = input[i, j] * results[i - 1, j];
                }
            }
            else
            {
                Parallel.ForEach(ParallelEnumerable.Range(0, input.GetLength(0)),
                        j => results[j, 0] = input[j, 0]);

                foreach(int i in Enumerable.Range(1, input.GetLength(1) - 1))
                {
                    for (int j = 0; j < input.GetLength(0); j++)
                        results[j, i] = input[j, i] * results[j, i - 1];
                }
            }

            return results;
        }
    }
}
